﻿using System;
using web_uchet.Models;
using System.Linq;
using System.Collections.Generic;
using web_uchet.ViewModels;
using web_uchet.Security;
using web_uchet.Security.Enums;
using System.Xml;
using System.Xml.Linq;
using System.IO;
using System.Xml.Xsl;
using System.Web.Mvc;
using System.Reflection;
using Reporting.AppDomainExt;
namespace web_uchet.Reports
{
    public class WebUchetReportProvider : System.MarshalByRefObject
    {
        public static System.AppDomain GetProviderInNewDomain(ProgramContext PContext, Компания Company,ref WebUchetReportProvider Result)
        {
            var setup = System.AppDomain.CurrentDomain.SetupInformation.Copy();//new AppDomainSetup();
            setup.ShadowCopyDirectories = System.Web.HttpContext.Current.Server.MapPath(@"/Reporting/ReportDlls/");
            System.AppDomain NewAppDomain = null;
            try
            {
                NewAppDomain =
                       System.AppDomain.CreateDomain(PContext.CompanyID.ToString() + "Domain", null, setup);
                string PhysicalRoot = System.Web.HttpContext.Current.Server.MapPath("~");
                string AssemblyPath = System.Web.HttpContext.Current.Server.MapPath(@"/Reporting/ReportDlls/" + Company.ReportDLLName);
                //NewAppDomain.SetData("APP_CONFIG_FILE", System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

                object[] ConstructorParams = new object[] { ModelWebUchetCon.SingleToneConnection.ConnectionString, PContext, PhysicalRoot };
                // Create an instance of RemoteObject:
                //public WebUchetReportProvider(ProgramContext SecurityContext,string PhysicalRoot)

                Result = NewAppDomain
                    .CreateInstanceFromAndUnwrap(AssemblyPath,
                    Company.ReportClassFullName, false, System.Reflection.BindingFlags.Default, null,
                    ConstructorParams, null, null) as WebUchetReportProvider;
                return NewAppDomain;
            }
            catch (Exception ex)
            {
                System.AppDomain.Unload(NewAppDomain);
                throw ex;
            }
        }
        public System.AppDomain GetProviderInNewDomain(ProgramContext PContext, ref WebUchetReportProvider Result)
        {
            Компания Company = this._Context.Компании.First(e=>e.ИД_Компании==PContext.CompanyID);
            var setup = System.AppDomain.CurrentDomain.SetupInformation.Copy();//new AppDomainSetup();
            setup.ShadowCopyDirectories = System.Web.HttpContext.Current.Server.MapPath(@"/Reporting/ReportDlls/");
            System.AppDomain NewAppDomain = null;
            try
            {
                NewAppDomain =
                       System.AppDomain.CreateDomain(PContext.CompanyID.ToString() + "Domain", null, setup);
                string PhysicalRoot = System.Web.HttpContext.Current.Server.MapPath("~");
                string AssemblyPath = System.Web.HttpContext.Current.Server.MapPath(@"/Reporting/ReportDlls/" + Company.ReportDLLName);
                //NewAppDomain.SetData("APP_CONFIG_FILE", System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);

                object[] ConstructorParams = new object[] { ModelWebUchetCon.SingleToneConnection.ConnectionString, PContext, PhysicalRoot };
                // Create an instance of RemoteObject:
                //public WebUchetReportProvider(ProgramContext SecurityContext,string PhysicalRoot)

                Result = NewAppDomain
                    .CreateInstanceFromAndUnwrap(AssemblyPath,
                    Company.ReportClassFullName, false, System.Reflection.BindingFlags.Default, null,
                    ConstructorParams, null, null) as WebUchetReportProvider;
                return NewAppDomain;
            }
            catch(Exception ex)
            {
                System.AppDomain.Unload(NewAppDomain);
                throw ex;
            }
        }
        [NonSerialized]
        private ModelWebUchetCon _Context;
        [NonSerialized]
        protected ProgramContext _SecurityContext;
        [NonSerialized]
        protected System.Text.Encoding ReportEncoding = System.Text.Encoding.Default;
        protected string _PhysicalRoot = string.Empty;
        public WebUchetReportProvider(ProgramContext SecurityContext,string PhysicalRoot)
        {
            this._Context = new ModelWebUchetCon(ModelWebUchetCon.SingleToneConnection);
            this._SecurityContext = SecurityContext;
            this._PhysicalRoot = PhysicalRoot;
            this.InitCompanyReporting();
        }
        public WebUchetReportProvider(string ConnectionString, ProgramContext SecurityContext, string PhysicalRoot)
        {
            //File.Open(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, FileMode.OpenOrCreate).
            //    Close();
            //System.Configuration.ConfigurationManager.OpenExeConfiguration(
            //    AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
            try
            {
                this._Context = new ModelWebUchetCon(ConnectionString);
                this._SecurityContext = SecurityContext;
                this._PhysicalRoot = PhysicalRoot;
                this.InitCompanyReporting();
            }
            catch (Exception ex)
            {
                System.AppDomain.Unload(System.AppDomain.CurrentDomain);
                throw ex;
            }
        }
        public WebUchetReportProvider(ModelWebUchetCon Context, ProgramContext SecurityContext, string PhysicalRoot)
        {
            this._Context = Context;
            this._SecurityContext = SecurityContext;
            this._PhysicalRoot = PhysicalRoot;
            this.InitCompanyReporting();
        }

        protected virtual void InitCompanyReporting()
        {
            this._CompanyReports.Add(new AdvertisementReport());
        }

        public virtual GeneralCompanyReport GetGeneralCompanyReport()
        {
            GeneralCompanyReport Report=new GeneralCompanyReport(
                this._Context.Компании.First(e=>e.ИД_Компании==_SecurityContext.CompanyID));

            Report.EmployeeCount = this._Context.Пользователи.Where(e => e.Компания.ИД_Компании == _SecurityContext.CompanyID).Count()-1;
            Report.ClientCount = this._Context.Клиенты.Where(e => e.Компания.ИД_Компании == _SecurityContext.CompanyID).Count()-1;

            var CompanyData=
            this._Context.Заказы.Include("Пользователь").Include("Валюта_Услуги").Where(e => e.Компания.ИД_Компании == _SecurityContext.CompanyID
                && (e.Дата_Выполнения.HasValue || e.Дата_Поступления.HasValue)
                &&e.Выполнено
                );

            var GroupedResultManager = from Заказ Order in CompanyData
                                       group Order by new
                                       {
                                           year = Order.Дата_Выполнения.HasValue?Order.Дата_Выполнения.Value.Year:
                                           Order.Дата_Поступления.Value.Year,
                                           month = Order.Дата_Выполнения.HasValue?Order.Дата_Выполнения.Value.Month:
                                           Order.Дата_Поступления.Value.Month,
                                           employee = Order.Менеджер,
                                           currency = Order.Валюта
                                       } into grouped1
                                       orderby grouped1.Key.year,grouped1.Key.month
                                       select grouped1;            

            foreach (var MonthManagerData in GroupedResultManager)
            {
                DateTime MonthYearDate = new DateTime(MonthManagerData.Key.year, MonthManagerData.Key.month, 1);
                if (!Report.FinanceDict.ContainsKey(MonthYearDate))
                {
                    Report.FinanceDict.Add(MonthYearDate, new MonthCompanyStatistic());
                }
                var MonthCompStat=Report.FinanceDict[MonthYearDate];
                IEnumerable<KeyValuePair<string, float?>> MoneyByCurrency=MonthManagerData.GroupBy(e => e.Валюта).Select
                    (e => new KeyValuePair<string, float?>(e.Key.Символ, e.Sum(ord => ord.Цена)));
                int OrderCount = MonthManagerData.Count();
                MonthCompStat.MonthEmployeeStatistics.Add(new MonthEmployeeStatistic()
                {
                    Пользователь = MonthManagerData.Key.employee,
                    Role=UserRoleEnum.Менеджер,
                    OrderCount = OrderCount,
                    MoneyByCurrency = MoneyByCurrency
                });
                MonthCompStat.orderCount += OrderCount;
                foreach (var item in MoneyByCurrency) 
                {
                    if (MonthCompStat.MoneyByCurrency.ContainsKey(item.Key))
                        MonthCompStat.MoneyByCurrency[item.Key] += (item.Value.HasValue?item.Value.Value:0);
                    else MonthCompStat.MoneyByCurrency.Add(item.Key, (item.Value.HasValue ? item.Value.Value : 0));
                }
            }
            var GroupedResultExecutive = from Заказ Order in CompanyData
                                         group Order by new
                                         {
                                             year = Order.Дата_Выполнения.HasValue ? Order.Дата_Выполнения.Value.Year :
                                           Order.Дата_Поступления.Value.Year,
                                             month = Order.Дата_Выполнения.HasValue ? Order.Дата_Выполнения.Value.Month :
                                             Order.Дата_Поступления.Value.Month,
                                             employee = Order.Исполнитель,
                                             currency = Order.Валюта
                                         } into grouped1
                                         orderby grouped1.Key.year, grouped1.Key.month
                                         select grouped1;

            foreach (var MonthExecutiveData in GroupedResultExecutive)
            {
                DateTime MonthYearDate = new DateTime(MonthExecutiveData.Key.year, MonthExecutiveData.Key.month, 1);
                if (!Report.FinanceDict.ContainsKey(MonthYearDate))
                {
                    Report.FinanceDict.Add(MonthYearDate, new MonthCompanyStatistic());
                }
                var MonthCompStat = Report.FinanceDict[MonthYearDate];
                IEnumerable<KeyValuePair<string, float?>> MoneyByCurrency = MonthExecutiveData.GroupBy(e => e.Валюта).Select
                    (e => new KeyValuePair<string, float?>(e.Key.Символ, e.Sum(ord => ord.Цена)));
                int OrderCount = MonthExecutiveData.Count();
                MonthCompStat.MonthEmployeeStatistics.Add(new MonthEmployeeStatistic()
                {
                    Пользователь = MonthExecutiveData.Key.employee,
                    Role = UserRoleEnum.Исполнитель,
                    OrderCount = OrderCount,
                    MoneyByCurrency = MoneyByCurrency
                });
            }
            return Report;
        }
        private XElement GetXMLOrder(Заказ Order)
        {
            ViewЗаказ ViewOrder = new ViewЗаказ(Order);
            XElement XOrder = new XElement("Заказ");
            XOrder.Add
                     (new XElement("ИД_Заказа", ViewOrder.ИД_Заказа),
                        new XElement("Дата_Пост", ViewOrder.Дата_Поступления.HasValue ?
                            ViewOrder.Дата_Поступления.Value.ToString("dd-MM-yyyy mm:ss") :
                            "без записи"),
                        new XElement("Клиент", ViewOrder.Клиент.Имя),
                        new XElement("Менеджер", ViewOrder.Менеджер.Имя_Настоящее),
                        new XElement("Исполнитель", ViewOrder.Исполнитель.Имя_Настоящее),
                        new XElement("Дата_Вып", ViewOrder.Дата_Назначения.HasValue ?
                            ViewOrder.Дата_Назначения.Value.ToString("dd-MM-yyyy hh:mm") :
                            "без записи"),
                        new XElement("Выполнил", ViewOrder.Исполнитель.Имя_Настоящее),
                        new XElement("Цена", ViewOrder.Цена.ToString() + " " + Order.Валюта.Символ),
                        new XElement("Скидка", ViewOrder.Скидка.ToString() + " %"),
                        new XElement("Цена_со_скидкой", ViewOrder.Цена_со_скидкой.ToString() + " " + ViewOrder.Валюта.Символ),
                        new XElement("Оплачено", ViewOrder.Оплачено.ToString() + " " + ViewOrder.Валюта.Символ),
                        new XElement("Долг", ViewOrder.Долг.ToString() + " " + ViewOrder.Валюта.Символ),
                        new XElement("Выполнен", ViewOrder.Выполнено),
                        new XElement("Отменен", ViewOrder.Отменен),
                        new XElement("Описание", ViewOrder.Описание)
                    );
            return XOrder;
        }
        /// <summary>
        /// Возвращает поток байтов - файл MSWord - отчет о заказе
        /// </summary>
        /// <param name="orderID"></param>
        /// <returns></returns>
        public virtual ReportResult GetOrderReport(int orderID)
        {
            Dictionary<Валюта, Single> OrderTotal = new Dictionary<Валюта, float>();

            var Order = //new ViewЗаказ(
                this._Context.Заказы.Include("Клиент").Include("Менеджер").Include("Исполнитель").Include("Валюта").Include("Детали.Валюта")
                .Include("Детали.Услуга").
                First(e => e.Компания.ИД_Компании == this._SecurityContext.CompanyID &&
                e.ИД_Заказа == orderID);
                //);

            XDocument XDoc = new XDocument();
            XElement XOrder = this.GetXMLOrder(Order);
            XDoc.Add(XOrder);
            
            XElement XDetails=new XElement("Детали");
            XOrder.Add(XDetails);
            foreach (var Service in Order.Детали)
            {
                XDetails.Add(new XElement("Услуга",
                    new XElement("Название", Service.Услуга.Название),
                    new XElement("Колличество",Service.Колличество),
                    new XElement("Цена", Service.Цена.ToString() + " " + Service.Валюта.Символ))
                    );
                if (OrderTotal.ContainsKey(Service.Валюта))
                    OrderTotal[Service.Валюта] += Service.Цена;
                else OrderTotal.Add(Service.Валюта, Service.Цена);
            }
            XElement XTotals = new XElement("Итоги");
            XOrder.Add(XTotals);
            foreach (var CurrencyTotal in OrderTotal)
            {
                    XTotals.Add(new XElement("Итог", 
                                    new XElement("Валюта", CurrencyTotal.Key.Символ), 
                                    new XElement("Цена", CurrencyTotal.Value.ToString()))
                        );  
            }

            XslCompiledTransform xslTran = new XslCompiledTransform(false);
            xslTran.Load(this._PhysicalRoot + "/Reporting/ReportTempl/OrderReport.xslt");
            MemoryStream OStream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(OStream, this.ReportEncoding);
            xslTran.Transform(XDoc.ToXmlDocument().CreateNavigator(), writer);
            
            byte[] Result=OStream.ToArray();
            OStream.Close();
            writer.Close();
            xslTran = null;
            writer = null;
            XDoc = null;
            return new ReportResult()
            {
                Report = Result,
                HeaderName = "Content-Disposition",
                HeaderValue = "attachment; filename=Otchet.rtf",
                ContentType = "application/msword"
            };
        }
        public virtual ReportResult PrintOrderTable(IEnumerable<Заказ> Data)
        {
            XDocument XDoc = new XDocument();
            XElement XOrders = new XElement("Заказы");
            XDoc.Add(XOrders);
            foreach (var Order in Data)
            {
                XElement XOrder = this.GetXMLOrder(Order);
                XOrders.Add(XOrder);
            }

            XslCompiledTransform xslTran = new XslCompiledTransform(false);
            xslTran.Load(this._PhysicalRoot + "/Reporting/ReportTempl/OrderTable.xslt");
            MemoryStream OStream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(OStream, this.ReportEncoding);
            xslTran.Transform(XDoc.ToXmlDocument().CreateNavigator(), writer);

            byte[] Result = OStream.ToArray();
            OStream.Close();
            writer.Close();
            xslTran = null;
            writer = null;
            XDoc = null;
            return new ReportResult()
            {
                Report = Result,
                HeaderName = "Content-Disposition",
                HeaderValue = "attachment; filename=Otchet.rtf",
                ContentType = "application/msword"
            };
        }
        public virtual ReportResult PrintClientTable(IEnumerable<Клиент> Data)
        {
            XDocument XDoc = new XDocument();
            XElement XClients = new XElement("Клиенты");
            XDoc.Add(XClients);
            foreach (var Client in Data)
            {
                XElement XClient =
                    new XElement("Клиент", new XElement("ФИО", Client.Имя),
                        new XElement("Компания", Client.Название_Компании),
                        new XElement("Должность", Client.Должность),
                        new XElement("Тел_Моб", Client.Тел_Моб),
                        new XElement("Тел_Раб", Client.Тел_Раб),
                        new XElement("EMail", Client.EMail),
                        new XElement("Skype", Client.Skype),
                        new XElement("Адрес", Client.Адрес),
                        new XElement("Контакты_Другие", Client.Контакты_Другие),
                        new XElement("Описание", Client.Описание));
                XClients.Add(XClient);
            }

            XslCompiledTransform xslTran = new XslCompiledTransform(false);
            xslTran.Load(this._PhysicalRoot + "/Reporting/ReportTempl/ClientTable.xslt");
            MemoryStream OStream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(OStream, this.ReportEncoding);
            xslTran.Transform(XDoc.ToXmlDocument().CreateNavigator(), writer);

            byte[] Result = OStream.ToArray();
            OStream.Close();
            writer.Close();
            xslTran = null;
            writer = null;
            XDoc = null;
            return new ReportResult()
            {
                Report = Result,
                HeaderName = "Content-Disposition",
                HeaderValue = "attachment; filename=Otchet.rtf",
                ContentType = "application/msword"
            };
        }

        #region "Отчеты компании"
        /// <summary>
        /// Регистрирует имя сборки этого класса  и название этого класса
        /// в базе
        /// </summary>
        public void RegisterCompanyDLL_in_DB(int CompanyID)
        {
            AssemblyName AsName = new AssemblyName(Assembly.GetAssembly(this.GetType()).FullName);
            var Company = this._Context.Компании.First(e => e.ИД_Компании == CompanyID);
            Company.ReportClassFullName = this.GetType().FullName;
            Company.ReportDLLName = AsName.Name + ".dll";
            this._Context.SaveChanges();
        }
        [NonSerialized]
        protected List<Report> _CompanyReports = new List<Report>();
        public List<ReportNameIDPair> GetReportNames() 
        { 
            return this._CompanyReports.Select(e => new ReportNameIDPair { Name=e.Name,ID= e.ID.ToString()}).ToList(); 
        }
        public List<ReportFilter> GetReportFilters(string ReportID)
        {
            Report Rep = this._CompanyReports.First(e => e.ID == ReportID);
            return Rep.Filters;
        }

        public ReportResult ExecuteReport(string ReportID, Dictionary<string, object> Filters)
        {
            Report Rep = this._CompanyReports.First(e => e.ID == ReportID);            
            return Rep.getReport(Filters,this._PhysicalRoot);
        }        
        #endregion
    }

    public class GeneralCompanyReport : ViewКомпания
    {
         public GeneralCompanyReport():base()
        {
        }
         public GeneralCompanyReport(Компания Base)
             : base(Base)
        {
        }
        private int _EmployeeCount;
        private long _ClientCount;      
        public int EmployeeCount { get { return this._EmployeeCount; } set { this._EmployeeCount = value; } }
        public long ClientCount { get { return this._ClientCount; } set { this._ClientCount = value; } }

        private Dictionary<DateTime, MonthCompanyStatistic>
           _FinanceDict = new Dictionary<DateTime, MonthCompanyStatistic>();
        public Dictionary<DateTime, MonthCompanyStatistic> FinanceDict 
        { get { return this._FinanceDict; } set { this._FinanceDict = value; } }

    }
    public class MonthCompanyStatistic
    {
        public int orderCount;
        public Dictionary<string, float> MoneyByCurrency=new Dictionary<string,float>();
        public List<MonthEmployeeStatistic> MonthEmployeeStatistics = new List<MonthEmployeeStatistic>();
    }
    public class MonthEmployeeStatistic
    {
        public Пользователь Пользователь;
        public UserRoleEnum Role;
        public int OrderCount;
        public IEnumerable<KeyValuePair<string, float?>> MoneyByCurrency;
    }
    public static class DocumentExtensions
    {
        public static XmlDocument ToXmlDocument(this XDocument xDocument)
        {
            var xmlDocument = new XmlDocument();
            using (var xmlReader = xDocument.CreateReader())
            {
                xmlDocument.Load(xmlReader);
            }
            return xmlDocument;
        }

        public static XDocument ToXDocument(this XmlDocument xmlDocument)
        {
            using (var nodeReader = new XmlNodeReader(xmlDocument))
            {
                nodeReader.MoveToContent();
                return XDocument.Load(nodeReader);
            }
        }
    }

    [Serializable]
    public class ReportResult
    {
        public byte[] Report;
        public string HeaderName;
        public string HeaderValue;
        public string ContentType;
    }
    [Serializable]
    public class ReportNameIDPair
    {
        public string Name { get; set; }
        public string ID { get; set; }
    }
    [Serializable]
    public class ReportFilter
    {
        public string DisplayName { get; set; }
        public string Name { get; set; }
        public ReportFilterControlTypes ControlType { get; set; }
        public ReportFilterDataTypes DataType { get; set; }
        public Dictionary<string, string> SelectList { get; set; }
    }
    [Serializable]
    public enum ReportFilterControlTypes
    {
        TextBox, SelectBox
    }
    [Serializable]
    public enum ReportFilterDataTypes
    {
        Numeric,String,DateTime
    }

    public abstract class Report
    {
        public Report()
        {
            this._Name = string.Empty;
            this._xslTransFileName = string.Empty;
            this._ID = string.Empty;
            this.ReportEncoding = System.Text.Encoding.Default;
        }
        public Report(System.Text.Encoding ReportEncoding)
        {
            this._Name = string.Empty;
            this._xslTransFileName = string.Empty;
            this._ID = string.Empty;
            this.ReportEncoding = ReportEncoding;
        }
        protected System.Text.Encoding ReportEncoding;
        protected string _xslTransFileName;
        protected string _Name;
        protected string _ID;
        public string Name { get { return this._Name; } }
        public string ID { get { return this._ID; } }
        public List<ReportFilter> Filters = new List<ReportFilter>();

        public virtual ReportResult getReport(IDictionary<string, object> Filters, string PhysicalRoot)
        {
            throw new NotImplementedException("Базовый класса отчетов не реализует функциональность");
        }
    }
    public class AdvertisementReport : Report
    {
        public AdvertisementReport()
        {
            this._Name = "Пример отчета";
            this._ID = "1";
            this._xslTransFileName = "Advertisement.xslt";
            this.Filters.Add(new ReportFilter() 
                {
                    ControlType=ReportFilterControlTypes.TextBox,
                    DisplayName="Напишите чего-нибудь", 
                    Name="WriteSmth",
                    DataType=ReportFilterDataTypes.Numeric 
                });

            var SelectList=new Dictionary<string, string>();
            SelectList.Add("Первый выбор", "1");
            SelectList.Add("Второй выбор", "2");
            this.Filters.Add(new ReportFilter()
            {
                ControlType = ReportFilterControlTypes.SelectBox,
                DisplayName = "Выберите чего-нибудь",
                Name="ChooseSmth",
                SelectList = SelectList
            });
        }
        public override ReportResult getReport(IDictionary<string, object> Filters, string PhysicalRoot)
        {
            XDocument XDoc = new XDocument();
            XDoc.Add(new XElement("Report",""));
            XslCompiledTransform xslTran = new XslCompiledTransform(false);
            xslTran.Load(PhysicalRoot + "/Reporting/ReportTempl/"+this._xslTransFileName);
            MemoryStream OStream = new MemoryStream();
            XmlTextWriter writer = new XmlTextWriter(OStream, this.ReportEncoding);
            xslTran.Transform(XDoc.ToXmlDocument().CreateNavigator(), writer);

            byte[] Result = OStream.ToArray();
            OStream.Close();
            writer.Close();
            xslTran = null;
            writer = null;
            XDoc = null;
            return new ReportResult()
            {
                Report = Result,
                HeaderName = "Content-Disposition",
                HeaderValue = "attachment; filename=Otchet.rtf",
                ContentType = "application/msword"
            };
        }
    }
}
